package net.qiqbframework.persisthanding;

import net.qiqbframework.common.Priority;
import net.qiqbframework.common.utils.AnnotationUtil;
import net.qiqbframework.messaging.Message;
import net.qiqbframework.messaging.annotation.HandlerParameterResolver;
import net.qiqbframework.messaging.annotation.HandlerParameterResolverFactory;
import net.qiqbframework.messaging.task.CurrentMessageTask;
import net.qiqbframework.messaging.task.MessageTask;

import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 批量加载参数解析
 */
@Priority(Priority.HIGH)
public class PersistPayloadParameterResolverFactory implements HandlerParameterResolverFactory {
    @Override
    public HandlerParameterResolver<?> createInstance(Executable executable, Parameter[] parameters, int parameterIndex) {
        Class<?> parameterType = parameters[parameterIndex].getType();
        final Optional<Map<String, Object>> persistHandlerAttrsOptional = AnnotationUtil.findAnnotationAttributes(executable, PersistHandler.class);
        boolean supportBatch = (boolean) persistHandlerAttrsOptional
                .map(attr -> attr.get("supportBatch"))
                .orElse(false);
        if (persistHandlerAttrsOptional.isPresent()) {
            if (parameterIndex == 0 && (Collection.class.isAssignableFrom(parameterType) || Map.class.isAssignableFrom(parameterType))) {



                //
                if (supportBatch) {

                    return new BatchPersistParameterResolver(parameterType);
                }
            }


            if (parameterIndex == 1) {
                return new HandlerParameterResolver<>() {
                    @Override
                    public boolean support(Message<?> message) {
                        return !supportBatch;
                    }

                    @Override
                    public Object resolveParameter(Message<?> message) {
                        if (message instanceof PersistMessage<?> persistMessage) {
                            return persistMessage.getSnapshotAggregateRoot();
                        }
                        return null;
                    }
                };
            }
        }
        return null;
    }

    public static class BatchPersistParameterResolver implements HandlerParameterResolver<Object> {
        private final Class<?> parameterType;

        public BatchPersistParameterResolver(Class<?> parameterType) {
            this.parameterType = parameterType;
        }

        @Override
        public Object resolveParameter(Message<?> message) {
            if (message instanceof PersistMessage<?> persistMessage) {
                if (Map.class.isAssignableFrom(parameterType)) {
                    Map<Object, Object> mapParam = new HashMap<>();
                    findNext(persistMessage, mapParam);
                    return mapParam;
                } else {
                    List<Object> batchParam = new ArrayList<>();
                    findNext(persistMessage, batchParam);

                    return batchParam;
                }

            }
            return null;
        }

        private void findNext(PersistMessage<?> persistMessage, Map<Object, Object> mapParam) {
            if (persistMessage == null) {
                return;
            }
            final Optional<MessageTask<?>> parent = CurrentMessageTask.get().parent();
            if (parent.isPresent()) {
                final Object persistAggregateRootCache = parent.get().getResource("_persistAggregateRootCache");
                if (persistAggregateRootCache != null &&
                        ((Set<?>) persistAggregateRootCache).contains(persistMessage.getPayload())) {
                    return;

                }
            }
            parent.ifPresent(messageTask -> messageTask.getOrComputeResource("_persistAggregateRootCache", r -> new HashSet<>()).add(persistMessage.getPayload()));

            mapParam.put(persistMessage.getPayload(), persistMessage.getSnapshotAggregateRoot());
            findNext(persistMessage.next(), mapParam);
        }

        private void findNext(PersistMessage<?> persistMessage, List<Object> batchParam) {
            if (persistMessage == null) {
                return;
            }
            final Optional<MessageTask<?>> parent = CurrentMessageTask.get().parent();
            if (parent.isPresent()) {
                final Object persistAggregateRootCache = parent.get().getResource("_persistAggregateRootCache");
                if (persistAggregateRootCache != null &&
                        ((Set<?>) persistAggregateRootCache).contains(persistMessage.getPayload())) {
                    return;

                }
            }
            parent.ifPresent(messageTask -> messageTask.getOrComputeResource("_persistAggregateRootCache", r -> new HashSet<>()).add(persistMessage.getPayload()));

            batchParam.add(persistMessage.getPayload());
            findNext(persistMessage.next(), batchParam);


        }

        @Override
        public boolean support(Message<?> message) {
            return message instanceof PersistMessage<?>;
        }

        @Override
        public Class<?> supportedPayloadType() {
            return Collection.class;
        }
    }

}
